from ast import arg
from multiprocessing import connection
import socket as sock
from time import sleep
import os
import threading
import struct
import json
import asyncio
import aiofiles

buff_size=1024

class server:
    def __init__(self,socket:sock.socket,port,ip_addres) -> None:
        print('initing...')
        self.socket=socket
        self.port=port
        self.socket.setblocking(False)
        self.socket.bind((ip_addres,self.port))
        self.socket.listen(5)
        self.client_list=[]

    def wait_connect(self):
        print('waiting for connect...')
        while True:
            try:
                connection,addr=self.socket.accept()
                self.client_list.append((connection,addr))
                print(f'get connection from{addr}...')
            except BlockingIOError:
                pass
            sleep(1)

    def get_request(self,aio=False):
        while True:
            for client_conn,client_addr in self.client_list:
                try:
                    buf=client_conn.recv(buff_size)
                    if buf.decode()=='getfile':
                        print('got request of sending file')
                        if not aio:
                            send_file=threading.Thread(target=server.get_file_name,args=(self,client_conn))
                            send_file.start()
                            send_file.join()
                        else:
                            asyncio.run(self.get_file_name_aio(client_conn))
                    if buf.decode()=='hi.txt':
                        print('wrong')
                except(BlockingIOError,ConnectionResetError):
                    pass
            sleep(1)

    def get_file_name(self,conn:sock.socket):
            path='./file'
            file_list=os.listdir(path)
            send_buf=''
            for i in file_list:
                send_buf+=i
                send_buf+='\r\n'
            self.send_pack(send_buf.encode(),conn)
            print('waiting for filename...')
            while True:
                try:
                    file_name=conn.recv(buff_size)
                    if file_name:
                        send_file=threading.Thread(target=server.send_file,args=(self,file_name.decode(),conn,path))
                        send_file.start()
                        send_file.join()
                        break
                    else:
                        continue
                except(BlockingIOError,ConnectionResetError):
                    ...
                

    async def get_file_name_aio(self,conn:sock.socket):
        path = './file'
        file_list = os.listdir(path)
        send_buf = ''
        for i in file_list:
            send_buf += i
            send_buf += '\r\n'
        self.send_pack(send_buf.encode(), conn)
        print('waiting for filename...')
        while True:
            print(1)
            try:
                file_name = conn.recv(buff_size)
                queue = asyncio.Queue()
                task = [asyncio.create_task(self.read_file_aio(queue, file_name.decode(), conn, path)),
                        self.send_file_aio(queue, conn)]
                await asyncio.gather(*task)
                break
            except(BlockingIOError, ConnectionResetError):
                ...

    async def read_file_aio(self,queue,filename:str,conn:sock.socket,path):
        print('sending file...')
        header_dic = {
            'filename': filename,
            'file_size': os.path.getsize(path + '/' + filename)
        }
        header_json = json.dumps(header_dic)
        header_bytes = header_json.encode('utf-8')
        conn.send(struct.pack('i', len(header_bytes)))
        conn.send(header_bytes)
        async with aiofiles.open(path + '/' + filename, 'rb') as file:
            data = await file.read()
            await queue.put(data)

    async def send_file_aio(self, queue, conn:sock.socket):
        """Copy from send_file
            Achieve aio.
        """
        loop = asyncio.get_running_loop()
        data = await queue.get()
        await loop.sock_sendall(conn, data)

    
    def send_file(self,filename:str,conn:sock.socket,path):
        print('sending file...')
        header_dic = {
            'filename': filename,
            'file_size': os.path.getsize(path+'/'+filename)  # 这里把文件的名字和地址结合在一起得到文件长度
        }      # 字典方便储存数据
        header_json = json.dumps(header_dic)  # 把字典转换成js格式(字符串类型)

        header_bytes = header_json.encode('utf-8')  

        # 第二步：先发送报头的长度
        conn.send(struct.pack('i', len(header_bytes)))  

        # 第三步：再发报头
        conn.send(header_bytes)

        with open(path+'/'+filename,'rb') as file:
            for i in file:
                conn.send(i)
        print('send finish')

    def send_pack(self,pack,conn):
        conn.send(pack)

def run():
    socket=sock.socket(sock.AF_INET, sock.SOCK_STREAM)
    s=server(socket,4444,'127.0.0.1')
    is_aio=False
    t1=threading.Thread(target=server.wait_connect,args=(s,))
    t2=threading.Thread(target=server.get_request,args=(s,is_aio))
    print('is_aio:',is_aio)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

if __name__=='__main__':
    run()